package juswaypay

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"sync"
	"time"
)

var (
	// 单例实例
	instance *JuswayPayClient
	once     sync.Once
	initErr  error
)

// JuswayPayClient 聚实汇支付平台客户端
type JuswayPayClient struct {
	config        *Config
	httpClient    *http.Client
	token         string
	tokenMu       sync.RWMutex
	stopChan      chan struct{}
	utils         *JuswayPayUtils
	refreshing    bool         // 是否正在刷新
	refreshMu     sync.Mutex   // 刷新互斥锁
	refreshTicker *time.Ticker // 刷新定时器
}

// GetJuswayPayClient 获取JuswayPayClient单例实例
// 参数:
//   - config: 平台配置信息
//
// 返回:
//   - *JuswayPayClient: 客户端实例
func GetJuswayPayClient(config *Config) *JuswayPayClient {
	once.Do(func() {
		utils := NewJuswayPayUtils(config)

		instance = &JuswayPayClient{
			config: config,
			httpClient: &http.Client{
				Timeout: time.Duration(config.Timeout) * time.Second,
			},
			stopChan: make(chan struct{}),
			utils:    utils,
		}

		// 初始化token
		fmt.Println("[JuswayPay] 正在初始化token...")
		if err := instance.updateToken(); err != nil {
			fmt.Printf("[JuswayPay] 初始化token失败: %v\n", err)
			initErr = err
		} else {
			fmt.Println("[JuswayPay] 初始化token成功")
		}
	})

	if initErr != nil {
		panic(fmt.Sprintf("JuswayPayClient初始化失败: %v", initErr))
	}

	return instance
}

// NewJuswayPayClient 创建聚实汇支付平台客户端（已废弃，请使用GetJuswayPayClient）
// 参数:
//   - config: 平台配置信息
//
// 返回:
//   - *JuswayPayClient: 客户端实例
func NewJuswayPayClient(config *Config) *JuswayPayClient {
	fmt.Println("警告: NewJuswayPayClient已废弃，请使用GetJuswayPayClient")
	return GetJuswayPayClient(config)
}

// updateToken 更新token
func (c *JuswayPayClient) updateToken() error {
	// 使用互斥锁确保同一时间只有一个刷新操作
	c.refreshMu.Lock()
	if c.refreshing {
		c.refreshMu.Unlock()
		return nil
	}
	c.refreshing = true
	c.refreshMu.Unlock()

	defer func() {
		c.refreshMu.Lock()
		c.refreshing = false
		c.refreshMu.Unlock()
	}()

	fmt.Printf("[JuswayPay] 开始更新token... 当前时间: %s\n", time.Now().Format("2006-01-02 15:04:05"))

	// 获取当前token
	currentToken := c.GetToken()
	var token, tokenType string
	var expiresIn int
	var err error

	// 如果有当前token，尝试刷新
	if currentToken != "" {
		fmt.Printf("[JuswayPay] 尝试刷新现有token... 当前时间: %s\n", time.Now().Format("2006-01-02 15:04:05"))
		token, tokenType, expiresIn, err = c.utils.RefreshAccessToken(currentToken)
		if err != nil {
			fmt.Printf("[JuswayPay] 刷新token失败: %v，尝试重新登录 当前时间: %s\n", err, time.Now().Format("2006-01-02 15:04:05"))
			// 如果刷新失败，尝试重新登录
			token, tokenType, expiresIn, err = c.utils.GetAccessToken()
		}
	} else {
		// 如果没有token，直接登录
		fmt.Printf("[JuswayPay] 没有现有token，尝试登录... 当前时间: %s\n", time.Now().Format("2006-01-02 15:04:05"))
		token, tokenType, expiresIn, err = c.utils.GetAccessToken()
	}

	if err != nil {
		return fmt.Errorf("get access token failed: %v", err)
	}

	fmt.Printf("[JuswayPay] 获取token成功: type=%s, expires_in=%d 当前时间: %s\n", tokenType, expiresIn, time.Now().Format("2006-01-02 15:04:05"))

	c.tokenMu.Lock()
	c.token = token
	c.tokenMu.Unlock()

	// 根据过期时间调整刷新间隔
	if expiresIn > 0 {
		// 使用配置的刷新时间，如果没有配置则使用默认值（过期前15分钟）
		refreshTime := c.config.RefreshTime
		if refreshTime <= 0 {
			refreshTime = 900 // 默认值：过期前15分钟
		}
		refreshInterval := time.Duration(expiresIn-refreshTime) * time.Second

		// 使用配置的最小刷新间隔，如果没有配置则使用默认值（1分钟）
		minRefreshTime := c.config.MinRefreshTime
		if minRefreshTime <= 0 {
			minRefreshTime = 300 // 默认值：5分钟
		}
		if refreshInterval < time.Duration(minRefreshTime)*time.Second {
			refreshInterval = time.Duration(minRefreshTime) * time.Second
		}
		fmt.Printf("[JuswayPay] 设置token刷新间隔: %v 当前时间: %s\n", refreshInterval, time.Now().Format("2006-01-02 15:04:05"))

		// 停止现有的定时器
		if c.refreshTicker != nil {
			c.refreshTicker.Stop()
		}

		// 创建新的定时器
		c.refreshTicker = time.NewTicker(refreshInterval)
		go c.startTokenRefresh()
	}

	return nil
}

// startTokenRefresh 启动定时刷新token
func (c *JuswayPayClient) startTokenRefresh() {
	maxRetries := 3 // 最大重试次数
	retryCount := 0

	for {
		select {
		case <-c.refreshTicker.C:
			fmt.Printf("[JuswayPay] 开始定时刷新token... 当前时间: %s\n", time.Now().Format("2006-01-02 15:04:05"))
			if err := c.updateToken(); err != nil {
				fmt.Printf("[JuswayPay] 刷新token失败: %v 当前时间: %s\n", err, time.Now().Format("2006-01-02 15:04:05"))
				retryCount++
				if retryCount >= maxRetries {
					fmt.Printf("[JuswayPay] 达到最大重试次数(%d),停止刷新token 当前时间: %s\n", maxRetries, time.Now().Format("2006-01-02 15:04:05"))
					return
				}
				// 失败后等待一段时间再重试
				time.Sleep(5 * time.Second)
			} else {
				retryCount = 0 // 重置重试计数
				fmt.Printf("[JuswayPay] token刷新成功 当前时间: %s\n", time.Now().Format("2006-01-02 15:04:05"))
			}
		case <-c.stopChan:
			fmt.Printf("[JuswayPay] 停止token刷新 当前时间: %s\n", time.Now().Format("2006-01-02 15:04:05"))
			if c.refreshTicker != nil {
				c.refreshTicker.Stop()
			}
			return
		}
	}
}

// Close 关闭客户端
func (c *JuswayPayClient) Close() {
	if c.refreshTicker != nil {
		c.refreshTicker.Stop()
	}
	select {
	case c.stopChan <- struct{}{}:
	default:
	}
}

// GetToken 获取当前 token
func (c *JuswayPayClient) GetToken() string {
	c.tokenMu.RLock()
	token := c.token
	c.tokenMu.RUnlock()
	return token
}

// doRequest 发送HTTP请求
func (c *JuswayPayClient) doRequest(ctx context.Context, method, path string, body interface{}) ([]byte, error) {
	// 构建请求URL
	reqURL := fmt.Sprintf("%s%s", c.config.GatewayURL, path)

	// 准备请求体
	var reqBody io.Reader
	if body != nil {
		jsonData, err := json.Marshal(body)
		if err != nil {
			return nil, fmt.Errorf("marshal request body failed: %v", err)
		}
		reqBody = bytes.NewReader(jsonData)
	}

	// 创建请求
	req, err := http.NewRequestWithContext(ctx, method, reqURL, reqBody)
	if err != nil {
		return nil, fmt.Errorf("create request failed: %v", err)
	}

	// 设置请求头
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("Authorization", "Bearer "+c.GetToken())

	// 发送请求
	resp, err := c.httpClient.Do(req)
	if err != nil {
		return nil, fmt.Errorf("do request failed: %v", err)
	}
	defer resp.Body.Close()

	// 读取响应
	respBody, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("read response failed: %v", err)
	}

	// 检查响应状态码
	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("request failed with status %d: %s", resp.StatusCode, string(respBody))
	}

	return respBody, nil
}

// Get 发送GET请求
func (c *JuswayPayClient) Get(ctx context.Context, path string) ([]byte, error) {
	return c.doRequest(ctx, http.MethodGet, path, nil)
}

// Post 发送POST请求
func (c *JuswayPayClient) Post(ctx context.Context, path string, body interface{}) ([]byte, error) {
	return c.doRequest(ctx, http.MethodPost, path, body)
}

// Put 发送PUT请求
func (c *JuswayPayClient) Put(ctx context.Context, path string, body interface{}) ([]byte, error) {
	return c.doRequest(ctx, http.MethodPut, path, body)
}

// Delete 发送DELETE请求
func (c *JuswayPayClient) Delete(ctx context.Context, path string) ([]byte, error) {
	return c.doRequest(ctx, http.MethodDelete, path, nil)
}

// GetConfig 获取配置信息
func (c *JuswayPayClient) GetConfig() *Config {
	return c.config
}
